<?php
class PermissionService extends FServiceBase
{
    public function findFolders($params)
    {
        $folder = $this->getParam($params, 'folder', '');
        $parentId = $this->getParam($params, 'parent_id', '');
        
        $folders = $this->findDirsRecursive($folder,'', $parentId, array('.svn'));
        Yii::trace(CVarDumper::dumpAsString($folders), 'Permission');
        
        return $this->result->processed('folders', $folders);
    }
    
    protected function findDirsRecursive($dir,$base, $parentId,$exclude)
    {
        $list=array();
        if (! is_dir($dir))
        {
            return $list;
        }
        $handle=opendir($dir);
        while(($file=readdir($handle))!==false)
        {
            if($file==='.' || $file==='..')
                continue;
            $path=$dir.DIRECTORY_SEPARATOR.$file;
            $isDir=is_dir($path);
            if($isDir && !in_array($file, $exclude))
            {
                $id = $parentId.'.'.$file;
                $list[] = array(
                    'text'=>'<span class="folder">'.$file.'</span>',
                    'id'=>$id,
                    'children'=>$this->findDirsRecursive($path,$base.'/'.$file,$id,$exclude),
                    'expanded'=>false,
                );
            }
        }
        closedir($handle);
        return $list;
    }
    
    public function findControllers($params)
    {
        $folder = $this->getParam($params, 'folder', '');
        $parentId = $this->getParam($params, 'parent_id', '');
        
        $files = CFileHelper::findFiles($folder, array(
            'fileTypes'=>array('php'),
            'exclude'=>array('.svn'),
            'level'=>0,
        ));
        
        $count = count($files);
        if ($count)
        {
            $files = array_map('str_replace', array_fill(1, $count, $folder), array_fill(1, $count, ''), $files);
            $files = array_map('substr', $files, array_fill(1, $count, 1));        
            $unset = array();
            foreach ($files as $index => $file)
            {
                include_once($folder.DIRECTORY_SEPARATOR.$file);
                list($controller,) = explode('.', $file);
                $class = $controller;
                $controller = str_replace('Controller', '', $controller);
                $id = empty($parentId) ? $controller : $parentId.'.'.$controller;
                $reflection = new ReflectionClass($class);
                $comments = $reflection->getDocComment();
                $visible = true;
                if (strlen($comments))
                {
                    $visible = $this->isVisible($comments);
                    $name = $this->getName($comments);
                    if (! empty($name))
                    {
                        $controller = $name;
                    }
                }
                if ($visible)
                {
                    $files[$index] = array('label'=>$controller,'url'=>'#','itemOptions'=>array('id'=>$id));
                }
                else
                {
                    $unset[] = $index;
                }
            }
            if (count($unset))
            {
                foreach ($unset as $index)
                {
                    unset($files[$index]);
                }
            }
        }
        
        return $this->result->processed('controllers', $files);
    }
    
    protected function convert($subFolders, $parentId)
    {
        if (is_array($subFolders) && count($subFolders))
        {
            $children = array();
            foreach ($subFolders as $key => $item)
            {
                if (is_array($item))
                {
                    $id = $parentId.'.'.$key;
                    $children[] = array(
                        'text'=>'<span class="folder">'.$key.'</span>',
                        'id'=>$id,
                        'children'=>$this->convert($item, $id),
                        'expanded'=>false,
                    );
                }
                else
                {
                    $id = str_replace('Controller', '', $item);
                    $id = empty($parentId) ? $id : $parentId.'.'.$id;
                    $children[] = array('text'=>$item, 'id'=>$id);
                }
            }
            return $children;
        }
        else
        {
            $id = str_replace('Controller', '', $subFolders);
            $id = empty($parentId) ? $id : $parentId.'.'.$id;
            return array('text'=>$subFolders, 'id'=>$id);
        }
    }
    
    public function findFolderTree()
    {
        $folders = array();
        //application controllers
        $folder = Yii::getPathOfAlias('application.controllers');
        $application = FSM::_run('Admin.permission.findFolders', array('folder'=>$folder, 'parent_id'=>''))->folders;
        $folders[] = array(
            'text'=>'<span class="folder">Application</span>',
            'id'=>'application',
            'children'=>$application,
            'expanded'=>false,
        );
        //modules controllers
        $modules = Yii::app()->getModules();
        $exclude = array('Core','gii');
        if (count($modules))
        {
            $modules = array_keys($modules);
            foreach ($modules as $moduleID)
            {
                if (in_array($moduleID, $exclude))
                    continue;
                $folder = Yii::getPathOfAlias($moduleID.'.controllers');
                $module = FSM::_run('Admin.permission.findFolders', array('folder'=>$folder, 'parent_id'=>$moduleID))->folders;
                $folders[] = array(
                    'text'=>'<span class="folder">'.$moduleID.'</span>',
                    'id'=>$moduleID,
                    'children'=>$module,
                    'expanded'=>false,
                );
            }
        }
        
        return $this->result->processed('folders', $folders);
    }
    
    public function findActions($params)
    {
        $controller = $this->getParam($params, 'controller', 'Cms.admin.test.Page');
        
        $actions = array();
        
        //append 'controllers'
        if (strpos($controller, '.') === false)
        {
            $controllerAlias = 'application.controllers.'.$controller;
        }
        else
        {
            list($first, $second) = explode('.', $controller, 2);
            $controllerAlias = $first.'.controllers.'.$second;
        }
        
        $controllerPath = Yii::getPathOfAlias($controllerAlias.'Controller').'.php';
        //Yii::trace('$controllerPath '.$controllerPath, 'Permission');
        if (file_exists($controllerPath))
        {
            $class = substr(strrchr($controllerAlias, "."), 1).'Controller';
            //Yii::trace('$class '.$class, 'Permission');
            include_once($controllerPath);
            //$methods = get_class_methods($class);
            $reflection = new ReflectionClass($class);
            $methods = $reflection->getMethods();
            //Yii::trace('$methods '.CVarDumper::dumpAsString($methods), 'Permission');
            if (is_array($methods) && count($methods))
            {
                foreach ($methods as $method)
                {
                    //Yii::trace('$method->name '.$method->name, 'Permission');
                    //Yii::trace('isFinal '.CVarDumper::dumpAsString($method->isFinal()), 'Permission');
                    if ($method->class == $class)
                    {
                        if ($method->name === 'actions')
                        {
                            //TODO: map actions
                        }
                        elseif (strpos($method->name, 'action') !== false)
                        {
                            $comments = $method->getDocComment();
                            //Yii::trace('$comments '.CVarDumper::dumpAsString($comments), 'Permission');
                            $name = $this->getName($comments);
                            $visible = $this->isVisible($comments);
                            $key = str_replace('action', '', $controller.'.'.$method->name);
                            if ($visible)
                            {
                                $actions[$key] = empty($name) ? $key : $name;
                            }
                        }
                    }
                }
            }
        }
        
        return $this->result->processed('actions', $actions);
    }
    
    protected function getName($comments)
    {
        $expr = '/@desc\s+(.+)/im';
        preg_match($expr, $comments, $matchs); //capture the comments 
        //Yii::trace('$matchs '.CVarDumper::dumpAsString($matchs), 'Permission');
        if (is_array($matchs) && count($matchs) == 2)
        {
            return $matchs[1];
        }
        return '';
    }
    
    protected function isVisible($comments)
    {
        $expr = '/@visible\s+(.+)/im';
        preg_match($expr, $comments, $matchs); //capture the comments 
        //Yii::trace('$matchs '.CVarDumper::dumpAsString($matchs), 'Permission');
        if (is_array($matchs) && count($matchs) == 2)
        {
            if (! empty($matchs[1]))
                $matchs[1] = trim($matchs[1]);
            if ($matchs[1] === 'false')
            {
                return false;
            }
            return (boolean) $matchs[1];
        }
        return true;
    }
    
    public function getRoles($params)
    {
        $roles = array();
        //setup default auth data
        /** @var CDbAuthManager */
        $authManager = Yii::app()->authManager;
        
        /*if ($authManager->getAuthItem(FAuthManager::ROLE_ADMINISTRATORS)===null)
        {
            $authManager->createRole(FAuthManager::ROLE_ADMINISTRATORS, 'Super administrator');
        }
        if ($authManager->getAuthItem(FAuthManager::ROLE_MANAGERS)===null)
        {
            $authManager->createRole(FAuthManager::ROLE_MANAGERS, '');
        }
        if ($authManager->getAuthItem(FAuthManager::ROLE_USERS)===null)
        {
            $authManager->createRole(FAuthManager::ROLE_USERS, 'Logged in users');
        }
        if ($authManager->getAuthItem(FAuthManager::ROLE_GUESTS)===null)
        {
            $authManager->createRole(FAuthManager::ROLE_GUESTS, '');
        }*/
        
        //add itemchild
        /*if (!$authManager->hasItemChild(FAuthManager::ROLE_ADMINISTRATORS, FAuthManager::ROLE_MANAGERS))
        {
            $authManager->addItemChild(FAuthManager::ROLE_ADMINISTRATORS, FAuthManager::ROLE_MANAGERS);
        }
        if (!$authManager->hasItemChild(FAuthManager::ROLE_MANAGERS, FAuthManager::ROLE_USERS))
        {
            $authManager->addItemChild(FAuthManager::ROLE_MANAGERS, FAuthManager::ROLE_USERS);
        }
        if (!$authManager->hasItemChild(FAuthManager::ROLE_USERS, FAuthManager::ROLE_GUESTS))
        {
            $authManager->addItemChild(FAuthManager::ROLE_USERS, FAuthManager::ROLE_GUESTS);
        }*/
        
        //assign user id = 1 to administrator
        /*if ($authManager->isAssigned(FAuthManager::ROLE_ADMINISTRATORS, 1) === false)
        {
            $authManager->assign(FAuthManager::ROLE_ADMINISTRATORS, 1);
        }*/
        
        $roles = $authManager->getRoles();
        ksort($roles);
        
        return $this->result->processed('roles', $roles);
    }
    
    public function getGrantedActions($params)
    {
        $itemName = $this->getParam($params, 'itemName', '');
        $actions = $this->getParam($params, 'actions', array());
        
        $grantedActions = array();
        if (count($actions))
        {
            foreach ($actions as $action => $name)
            {
                $status = FSM::_run('Admin.permission.hasItemChild', array('itemName'=>$itemName,'childName'=>$action))->status;
                if ($status)
                {
                    /*if (strpos($name, '.') !== false)
                        $name = substr(strrchr($name, "."), 1);
                    array_push($grantedActions, $name);*/
                    array_push($grantedActions, $action);
                }
            }
        }
        
//        $grantedActions = implode(', ', $grantedActions);
        
        return $this->result->processed('actions', $grantedActions);
    }
    
    public function hasItemChild($params)
    {
        $itemName = $this->getParam($params, 'itemName', '');
        $childName = $this->getParam($params, 'childName', '');
        $status = $this->hasItemChildRecursive($itemName, $childName);
        return $this->result->processed('status', $status);
    }
    
    protected function hasItemChildRecursive($itemName, $childName)
    {
        /** @var CDbAuthManager */
        $authManager = Yii::app()->authManager;
        $result = $authManager->hasItemChild($itemName, $childName);
        if ($result === false)
        {
            $children = $authManager->getItemChildren($itemName);
            if (is_array($children) && count($children))
            {
                foreach ($children as $child)
                {
                    $result = $this->hasItemChildRecursive($child->name, $childName);
                    if ($result === true)
                        return true;
                }
            }
        }
        return $result;
    }
    
    public function save($params)
    {
        $role = $this->getParam($params, 'role', '');
        $actions = $this->getParam($params, 'actions', array());
        $removeActions = $this->getParam($params, 'remove_actions', array());
        
        /** @var CDbAuthManager */
        $authManager = Yii::app()->authManager;
        if (is_array($actions) && count($actions) && $authManager->getAuthItem($role) !== null)
        {
            foreach ($actions as $action)
            {
                if (! $authManager->hasItemChild($role, $action))
                {
                    if ($authManager->getAuthItem($action) === null)
                        $authManager->createAuthItem($action, FAuthManager::ACTION_ITEM_TYPE);
                    $authManager->addItemChild($role, $action);
                }
            }
        }
        if (count($removeActions))
        {
            foreach ($removeActions as $action)
            {
                if ($authManager->hasItemChild($role, $action))
                {
                    $authManager->removeItemChild($role, $action);
                }
            }
        }
        
        return $this->result;
    }
    
    public function deleteAuthItem($params)
    {
        $name = $this->getParam($params, 'name', '');
        
        /** @var CDbAuthManager */
        $authManager = Yii::app()->authManager;
        $status = $authManager->removeAuthItem($name);
        
        return $this->result->processed('status', $status);
    }
    
    public function getRolesAvailableParent($params)
    {
        $role = $this->getParam($params, 'role', '');
        $selected = $parents = array();
        
        $roles = FSM::run('Admin.permission.getRoles')->roles;
        if (empty($role) == false){
            /** @var CDbAuthManager */
            $authManager = Yii::app()->authManager;
            foreach ($roles as $item)
            {
                if ($item->name === $role) continue;
                $status = FSM::run('Admin.permission.hasItemChild', array('itemName'=>$role, 'childName'=>$item->name))->status;
                if ($status == false)
                {
                    $parents[] = $item->name;
                    if ($authManager->hasItemChild($item->name, $role))
                    {
                        $selected[] = $item->name;
                    }
                }
            }
        }
        if (count($parents))
        {
            $parents = array_combine($parents, $parents);
        }
        
        $this->result->processed('selected', $selected);
        return $this->result->processed('roles', $parents);
    }
    
    public function updateParents($params)
    {
        $role = $this->getParam($params, 'role', '');
        $selected = $this->getParam($params, 'selected', array());
        
        $parents = FSM::_run('Admin.permission.getRolesAvailableParent', array('role'=>$role))->roles;
        Yii::trace('$parents '.CVarDumper::dumpAsString($parents), 'Permission');
        Yii::trace('$selected '.CVarDumper::dumpAsString($selected), 'Permission');
        Yii::trace('$role '.CVarDumper::dumpAsString($role), 'Permission');
        
        $status = false;
        if (count($parents))
        {
            /** @var CDbAuthManager */
            $authManager = Yii::app()->authManager;
            foreach ($parents as $parent)
            {
                if ($authManager->hasItemChild($parent, $role))
                {
                    if (count($selected) <= 0 || in_array($parent, $selected) === false)
                    {
                        //remove
                        $status = $authManager->removeItemChild($parent, $role);
                        Yii::trace('removeItemChild '.$parent.', '.$role, 'Permission');
                    }
                }
                elseif (count($selected) > 0 && in_array($parent, $selected)){
                    //add new
                    $status = $authManager->addItemChild($parent, $role);
                }
            }
        }
        
        return $this->result->processed('status', $status);
    }
    
    public function getAssigned($params)
    {
        $roles = $this->getParam($params, 'roles', array(), 'xss,tag,newline');
        $userId = $this->getParam($params, 'userId', 0);
        $assigned = array();
        
        /** @var CDbAuthManager */
        $authManager = Yii::app()->authManager;
        if (count($roles))
        {
            foreach ($roles as $role)
            {
                /** @var CAuthItem */
                if ($role->isAssigned($userId))
                {
                    $assigned[] = $role->name;
                }
            }
        }
        
        return $this->result->processed('assigned', $assigned);
    }
    
    public function updateAssign($params)
    {
        $role = $this->getParam($params, 'role', '');
        $userId = $this->getParam($params, 'userId', 0);
        $assign = $this->getParam($params, 'assign', 0);
        $status = false;
        
        /** @var CDbAuthManager */
        $authManager = Yii::app()->authManager;
        $authItem = $authManager->getAuthItem($role);
        if ($authItem !== null)
        {
            if ($assign)
            {
                if ($authItem->isAssigned($userId) == false)
                {
                    $status = $authManager->assign($role, $userId);
                }
            }
            else
            {
                if ($authItem->isAssigned($userId))
                {
                    $status = $authManager->revoke($role, $userId);
                }
            }
            
        }
        
        return $this->result->processed('status', $status);
    }
}